package edu.tec.mty.core.modules.models;

import java.util.HashMap;
import java.util.Vector;

import edu.tec.mty.core.modules.Self;
import edu.tec.mty.core.modules.World;

public class WorldModelFactory {

	private int maxNumberOfMember = 32;
	private int lifeSpan = 16;
//	private Vector<HashMap<Integer, Member>> mates = new Vector<HashMap<Integer, Member>>();
//	private Vector<HashMap<Integer, Member>> oppts = new Vector<HashMap<Integer, Member>>();
//	private Vector<HashMap<Integer, Member>> others = new Vector<HashMap<Integer, Member>>();
	private Vector<HashMap<Integer, Player>> players = new Vector<HashMap<Integer, Player>>();
	private int currentHistorySize = 0;
	private int nextID = 0;
	
	public WorldModelFactory() {
		
	}
	
//	private void updateStacks() {
//		if (this.currentHistorySize == this.maxNumberOfMember - 1) {
//			HashMap<Integer, Member> lastMatesOnStack = this.mates.get(this.maxNumberOfMember - 2);
//			for (Member member : lastMatesOnStack.values()) {
//				member.unbind();
//			}
//			HashMap<Integer, Member> lastOpptsOnStack = this.oppts.get(this.maxNumberOfMember - 2);
//			for (Member member : lastOpptsOnStack.values()) {
//				member.unbind();
//			}
//			HashMap<Integer, Member> lastOthersOnStack = this.others.get(this.maxNumberOfMember - 2);
//			for (Member member : lastOthersOnStack.values()) {
//				member.unbind();
//			}
//			this.mates.remove(maxNumberOfMember - 1);
//			this.oppts.remove(maxNumberOfMember - 1);
//			this.others.remove(maxNumberOfMember - 1);
//		} else {
//			this.currentHistorySize++;
//		}
//	}
	
	private void checkForPlayerStatus(HashMap<Integer, Player> players) {
		if (Self.STATUS == Self.WILL_TO_DASH) {
			for (Player member : players.values()) {
				member.clearVelocity();
			}
		}
	}
	
//	private void updatePlayers(Vector<Member> newPlayers, Vector<HashMap<Integer, Member>> playersHistory) {
//		HashMap<Integer, Member> playersForThisCycle = new HashMap<Integer, Member>();
//		if (playersHistory.size() == 0) {
//			for (int i = 0; i < newPlayers.size(); i++) {
//				Member member = newPlayers.get(i);
//				int ID = this.nextID++;
//				member.setID(ID);
//				playersForThisCycle.put(ID, member);
//			}
//		} else {
//			HashMap<Integer, Member> firstPlayersOnStack = playersHistory.get(0);
//			for (int i = 0; i < newPlayers.size(); i++) {
//				//nearest mate
//				double minDistance = 0.0;
//				int IDForMinDistance = -1;
//				int counter = 0;
//				int uniformNumber = newPlayers.get(i).getUniformNumber();
//				boolean isTheSamePlayer = false;
//				for (Member member : firstPlayersOnStack.values()) {
//					double number = member.getUniformNumber();
//					if (number == uniformNumber) {
//						isTheSamePlayer = true;
//						IDForMinDistance = member.getID();
//					}
//					if (!isTheSamePlayer) {
//						double distance = member.getCurrentPosition().getDistanceTo(newPlayers.get(i).getCurrentPosition());
//						if (counter == 0) {
//							minDistance = distance;
//							IDForMinDistance = member.getID();
//						} else {
//							if (distance < minDistance) {
//								minDistance = distance;
//								IDForMinDistance = member.getID();
//							}
//						}
//					}
//					counter++;
//				}
//				if (IDForMinDistance != -1) {
//					if (isTheSamePlayer) { //if it is the same uniform number
//						newPlayers.get(i).setID(IDForMinDistance);
//						playersForThisCycle.put(IDForMinDistance, newPlayers.get(i));
//					} else if (minDistance < this.threshold) { //check for threshold
//						newPlayers.get(i).setID(IDForMinDistance);
//						playersForThisCycle.put(IDForMinDistance, newPlayers.get(i));
//					}
//				} else { //it is a new player in sight
//					int nextID = this.nextID++;
//					newPlayers.get(i).setID(nextID);
//					newPlayers.get(i).clearVelocity();
//					playersForThisCycle.put(nextID, newPlayers.get(i));
//				}
//			}
//			//check for the history of objects not seen
//			for (Member member : firstPlayersOnStack.values()) {
//				int ID = member.getID();
//				if (!playersForThisCycle.containsKey(ID)) {
//					if (member.getCountToDeath() < this.lifeSpan) {
//						Member copyMember = member.clone();
//						copyMember.setNotViewed();
//						playersForThisCycle.put(ID, copyMember);
//					}
//				}
//			}
//		}
//		playersHistory.add(0, playersForThisCycle);
//	}
	
	public void bindPlayers(Vector<Player> players) {
		HashMap<Integer, Player> playersForThisCycle = new HashMap<Integer, Player>();
		HashMap<Integer, Integer> playersBound = new HashMap<Integer, Integer>();
		HashMap<Integer, Player> firstPlayersOnStack = this.players.get(0);
		for (int i = 0; i < players.size(); i++) {
			int teamID = players.get(i).getTeamID();
			int unumber = players.get(i).getUniformNumber();
			int IDForMinDistance = -1;
			double minDistance = -1.0;
			boolean isTheSamePlayer = false;
			int counter = 0;
			for (Player member : firstPlayersOnStack.values()) {
				int memberUnumber = member.getUniformNumber();
				int memberTeamID = member.getTeamID();
				if ((teamID == memberTeamID && teamID != 2) && (unumber == memberUnumber &&  unumber != -1)) {
					isTheSamePlayer = true;
					IDForMinDistance = member.getID();
					break;
				}
				double distance = member.getCurrentPosition().getDistanceTo(players.get(i).getCurrentPosition());
				if (counter == 0) {
					minDistance = distance;
					IDForMinDistance = member.getID();
				} else {
					if (distance < minDistance) {
						minDistance = distance;
						IDForMinDistance = member.getID();
					}
				}
				counter++;
			}
			if (IDForMinDistance != -1) {
				//the bindTo method copy some fields of the bound member, like teamID and uniform number
				if (isTheSamePlayer) {
					players.get(i).setID(IDForMinDistance);
					players.get(i).bindTo(firstPlayersOnStack.get(IDForMinDistance));
					playersForThisCycle.put(IDForMinDistance, players.get(i));
					playersBound.put(IDForMinDistance, 0); //consistent bound
				} else {
					Player memberToBind = firstPlayersOnStack.get(IDForMinDistance);
					if (memberToBind.isInThreshold(players.get(i).getCurrentPosition())) {
						if (memberToBind.getTeamID() == players.get(i).getTeamID()) {
							players.get(i).setID(IDForMinDistance);
							players.get(i).bindTo(memberToBind);
							playersBound.put(IDForMinDistance, 1); //normal bound
						} else if (players.get(i).getTeamID() == 2 || memberToBind.getTeamID() == 2) {
							players.get(i).setID(IDForMinDistance);
							players.get(i).bindTo(memberToBind);
							playersBound.put(IDForMinDistance, 2); //soft bound
						} else {
							//inconsistent bound
						}
						playersForThisCycle.put(IDForMinDistance, players.get(i));
					} else {
						//something else, maybe it is a new player in sight?
						int nextID = this.nextID++;
						players.get(i).setID(nextID);
						players.get(i).clearVelocity();
						playersForThisCycle.put(nextID, players.get(i));
					}
				}
			} else { //it is a new player in sight
				int nextID = this.nextID++;
				players.get(i).setID(nextID);
//				players.get(i).clearVelocity();
				playersForThisCycle.put(nextID, players.get(i));
			}
		}
		//TODO: do something with the bounds... I don't know what yet
		for (Player member : firstPlayersOnStack.values()) { //check for the history of objects not seen
			int ID = member.getID();
			if (!playersForThisCycle.containsKey(ID)) {
				if (member.getCountToDeath() < this.lifeSpan) {
					Player copyMember = member.clone();
					copyMember.bindTo(member);
					copyMember.setNotViewed();
					playersForThisCycle.put(ID, copyMember);
				}
			}
		}
//		this.checkForPlayerStatus(playersForThisCycle);
		this.players.add(0, playersForThisCycle);
	}
	
	public void updateAllPlayers(Vector<Player> players) {
		if (this.currentHistorySize == this.maxNumberOfMember) {
			HashMap<Integer, Player> lastPlayersOnStack = this.players.get(this.maxNumberOfMember - 2);
			for (Player member : lastPlayersOnStack.values()) {
				member.unbind();
			}
			this.players.remove(this.maxNumberOfMember - 1);
		} else {
			this.currentHistorySize++;
		}
		if (this.players.size() == 0) {
			HashMap<Integer, Player> playersOnThisCycle = new HashMap<Integer, Player>();
			for (int i = 0; i < players.size(); i++) {
				Player member = players.get(i);
				int ID = this.nextID++;
				member.setID(ID);
				playersOnThisCycle.put(ID, member);
			}
			this.checkForPlayerStatus(playersOnThisCycle);
			this.players.add(0, playersOnThisCycle);
		} else {
			this.bindPlayers(players);
		}
	}
	
//	public void updateAllPlayers(Vector<Member> others, Vector<Member> mates, Vector<Member> oppts) {
//		this.updateStacks();
//		this.updatePlayers(mates, this.mates);
//		this.updatePlayers(oppts, this.oppts);
//		HashMap<Integer, Member> othersForThisCycle = new HashMap<Integer, Member>();
//		if (this.others.size() == 0) {
//			for (int i = 0; i < others.size(); i++) {
//				Member member = others.get(i);
//				int ID = this.nextID++;
//				member.setID(ID);
//				othersForThisCycle.put(ID, member);
//			}
//		} else {
//			//a lot of code..........
//			Vector<Integer> indexToDelete = new Vector<Integer>();
//			for (int i = 0; i < others.size(); i++) {
//				int from = 0; // 0: mates, 1: oppts, 2: others
//				int IDForMinDistance = -1;
//				double minDistance = -1.0;
//				int counter = 0;
//				HashMap<Integer, Member> firstPlayersOnStack = this.mates.get(0);
//				for (Member member : firstPlayersOnStack.values()) {
//					
//				}
//			}
//			for (int i = 0; i < indexToDelete.size(); i++) {
//				others.remove(indexToDelete.get(i));
//			}
//		}
//		this.others.add(0, othersForThisCycle);
//	}
	
	public WorldModel getWorldModel() {
		Vector<Player> allMates = new Vector<Player>();
		Vector<Player> allOppts = new Vector<Player>();
		Vector<Player> allPlayers = new Vector<Player>();
		if (this.players.size() > 0) {
			HashMap<Integer, Player> firstPlayersOnStack = this.players.get(0);
			for (Player member : firstPlayersOnStack.values()) {
				switch (member.getTeamID()) {
				case 0:
					allMates.add(member);
					break;
				case 1:
					allOppts.add(member);
					break;
				}
				allPlayers.add(member);
			}
		}
		return new WorldModel(new Player[11], new Player[11], allMates, allOppts, allPlayers, World.getGameTimestamp());
		//return new WorldModel();
	}
	
	//Example
	/*public void addMembers(Member[] mates, Member[] opponnents) {
		if (this.mates.size() == this.maxNumberOfMember) {
			this.mates.remove(this.maxNumberOfMember - 1);
			Member[] matesTemp = this.mates.get(this.maxNumberOfMember - 2);
			for (int i = 0; i < matesTemp.length; i++) {
				if (matesTemp[i] != null) {
					matesTemp[i].setPointTo(null);
				}
			}
			this.mates.add(0, mates);
		} else {
			
		}
	}*/
	
}
